﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Reflection;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;
using VisualWip.Core;
using VisualWip.Core.Columns;
using VisualWip.Core.Interfaces;
using VisualWip.PresentationModel;
using VisualWip.PresentationModel.Group;
using VisualWip.PresentationModel.Image;
using VisualWip.PresentationModel.Line;
using VisualWip.PresentationModel.Text;
using VisualWip.PresentationModel.Uri;
using VisualWip.PresentationModel.ViewModels;
using VisualWip.PresentationModel.WorkItem;
using VisualWip.Properties;
using VisualWip.Util;

namespace VisualWip
{
    public class BootStrapper
    {
        private readonly IUnityContainer _container = new UnityContainer();
        private Application _app;
        private CompositionContainer _compositionContainer;

        public BootStrapper()
            : this(new string[] {})
        {
        }

        public BootStrapper(string[] args)
        {
            LoadContainerRegistrations();
            LoadViewModelRegistrations();
            InitApplication();
            HandleArguments(args);
        }

        [ImportMany(typeof (IColumnProviderViewModel))]
        public IEnumerable<IColumnProviderViewModel> ColumnProviderViewModels { get; set; }

        [Import(typeof (IBoardRecorder), AllowDefault = true)]
        public IBoardRecorder BoardRecorder { get; set; }

        [Import(typeof (IPersistanceProvider))]
        public IPersistanceProvider PersistanceProvider { get; set; }

        [Import(typeof (IPersistanceTreeSerializer))]
        public IPersistanceTreeSerializer PersistanceTreeSerializer { get; set; }

        [Import(typeof (IColumnBuilder))]
        public IColumnBuilder ColumnBuilder { get; set; }

        public Application Application
        {
            get { return _app; }
        }

        private void HandleArguments(string[] args)
        {
            if (args.Length <= 0) return;

            string path = (from arg in args
                           where arg.StartsWith("/") == false
                           select arg).FirstOrDefault();


            LoadBoardSettings(path);
        }

        private void LoadViewModelRegistrations()
        {
            var viewModelFactory = _container.Resolve<IViewModelFactory>();
            viewModelFactory.RegisterViewModel<WorkColumnBase, WorkColumnViewModel>();


            foreach (IColumnProviderViewModel provider in ColumnProviderViewModels)
            {
                provider.RegisterViewModels(viewModelFactory);
            }

            viewModelFactory.RegisterViewModel<WorkInProgressGroupColumn, WorkInProgressGroupViewModel>();

            viewModelFactory.RegisterViewModel<RichTextColumn, RichTextColumnViewModel>();
            viewModelFactory.RegisterViewModel<UriColumn, UriColumnViewModel>();
            viewModelFactory.RegisterViewModel<ImageColumn, ImageColumnViewModel>();
            viewModelFactory.RegisterViewModel<GroupColumn, GroupColumnViewModel>();
            viewModelFactory.RegisterViewModel<LineColumn, LineColumnViewModel>();
            viewModelFactory.RegisterViewModel<SingleWorkItemColumn, SingleWorkItemViewModel>();

            viewModelFactory.RegisterSettingsViewModel<RichTextColumn, RichTextColumnSettingsViewModel>();
            viewModelFactory.RegisterSettingsViewModel<UriColumn, UriColumnSettingsViewModel>();
            viewModelFactory.RegisterSettingsViewModel<ImageColumn, ImageColumnSettingsViewModel>();
            viewModelFactory.RegisterSettingsViewModel<GroupColumn, GroupColumnSettingsViewModel>();
            viewModelFactory.RegisterSettingsViewModel<LineColumn, LineColumnSettingsViewModel>();


            viewModelFactory.RegisterViewModel<ISettingsPath, RecentSettingsViewModel>();
        }

        private void InitApplication()
        {
            _container.Resolve<IWindowManager>();
            _app = _container.Resolve<Application>();
            _app.Init(_container.Resolve<IList<ISettingsPath>>(), _container.Resolve<IPersistanceProvider>());
        }

        private void LoadContainerRegistrations()
        {
            var map = new ExeConfigurationFileMap {ExeConfigFilename = "unity.config"};
            Configuration config = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);
            var section = (UnityConfigurationSection) config.GetSection("unity");

            section.Configure(_container);


            _container.RegisterType<IViewModelFactory, ViewModelFactory>(new ContainerControlledLifetimeManager());
            _container.RegisterType<Application>(new ContainerControlledLifetimeManager());
            _container.RegisterType<IWindowManager, WindowManager>(new ContainerControlledLifetimeManager());
            _container.RegisterType<IColumnBuilder, ColumnBuilder>();
            _container.RegisterType<ISettingsWindow, ColumnSettingsWindow>();
            _container.RegisterType<IWorkItemSizeProvider, StaticWorkItemSizeProvider>();
            _container.RegisterType<IColumnTypeProvider, StaticColumnTypeProvider>();
            _container.RegisterType<IZoomManager, ZoomManager>();

            ComposeProviders();
            LoadSetting(_container);
        }

        private void LoadSetting(IUnityContainer unityContainer)
        {
            if (Settings.Default.RecentSettingPaths != null)
            {
                var result = new List<ISettingsPath>();
                var recentSettingPaths = Settings.Default.RecentSettingPaths;
                foreach (string recentSettingPath in recentSettingPaths)
                {
                    try
                    {
                        using (var ms = new MemoryStream())
                        {
                            using (var writer = new StreamWriter(ms))
                            {
                                writer.Write(recentSettingPath);
                                writer.Flush();
                                ms.Position = 0;

                                PersistenceTree tree = PersistanceTreeSerializer.Load(ms);
                                var x = _container.Resolve(tree.Type) as ISettingsPath;
                                if (x == null)
                                    continue;

                                x.Load(tree);

                                result.Add(x);
                            }
                        }
                    }
                    catch (Exception)
                    {
                    }
                }
                var settingsPaths = result.Distinct(new SettingsPathComparer()).ToList();
                unityContainer.RegisterInstance<IList<ISettingsPath>>(settingsPaths);
            }
            else
            {
                unityContainer.RegisterInstance<IList<ISettingsPath>>(new List<ISettingsPath>());
            }
        }

        private void ComposeProviders()
        {
            string binFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            var dc = new DirectoryCatalog(binFolder);

            _compositionContainer = new CompositionContainer(dc);
            var batch = new CompositionBatch();
            batch.AddPart(this);
            batch.AddExportedValue(_container);
            batch.AddExportedValue(_app);
            var columnBuilder = _container.Resolve<IColumnBuilder>();
            batch.AddExportedValue(columnBuilder);

            _compositionContainer.Compose(batch);
            _container.RegisterInstance(PersistanceTreeSerializer);
            _container.RegisterInstance(PersistanceProvider);

            foreach (IColumnProviderViewModel providerViewModel in ColumnProviderViewModels)
            {
                providerViewModel.Init();
                _container.RegisterInstance(providerViewModel.ProviderName, providerViewModel);
            }

            _container.RegisterInstance(BoardRecorder ?? new DoNothingRecorder());
            _container.RegisterInstance(ColumnProviderViewModels);
        }

        public void SaveSettings()
        {
            Settings.Default.RecentSettingPaths = new StringCollection();

            foreach (ISettingsPath recentSettingsPath in _app.RecentSettingsPaths)
            {
                PersistenceTree setting = recentSettingsPath.Persist("RecentSetting");

                using (var ms = new MemoryStream())
                {
                    PersistanceTreeSerializer.Save(ms, setting);
                    ms.Position = 0;
                    using (var reader = new StreamReader(ms))
                    {
                        string text = reader.ReadToEnd();
                        Settings.Default.RecentSettingPaths.Add(text);
                    }
                }
            }
            Settings.Default.Save();
        }

        public void LoadBoardSettings(string path)
        {
            if (!string.IsNullOrWhiteSpace(path))
            {
                _app.Load(PersistanceProvider.LoadFromPathString(path));
            }
        }
    }
}